#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include "drvUART.h"

///////////////////////////////////////////////////////////////////////////////
// Constants & Macros
#define UART_BAUD_SELECT       (F_CPU/(drvUART_BAUD_RATE*16l)-1)

///////////////////////////////////////////////////////////////////////////////
// Module local variables
dosChar g_rx_buffer[drvUART_RX_BUFFER_LENGTH];
dosChar g_tx_buffer[drvUART_TX_BUFFER_LENGTH];

static volatile dosChar l_tx_push_index = 0;			// transmission circular buffer
static volatile dosChar l_tx_pop_index = 0;
static volatile dosByte l_rx_index = 0;						// simple buffer
static volatile dosBool l_line_received = dosFalse;

///////////////////////////////////////////////////////////////////////////////
// Initialize UART
void initUART(void)
{
	UBRRH = HIGH(UART_BAUD_SELECT);
	UBRRL = LOW(UART_BAUD_SELECT);
	UCSRA = 0;
	UCSRB = _BV(RXCIE) | _BV(RXEN) | _BV(TXEN);
	UCSRC = _BV(URSEL) | _BV(UCSZ1) | _BV(UCSZ0); 

	DDRD = 64+32+128;
	PORTD = 0;

}

/*****************************************************************************/
/* Transmitter section                                                       */
/*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
// Store dosChar in the transmission buffer
void drvUARTSendChar( dosChar in_dosChar )
{
	// buffer pointer
	dosByte new_push_index = l_tx_push_index + 1;

	if( new_push_index >= drvUART_TX_BUFFER_LENGTH )
		new_push_index = 0;

	// store dosCharacter
	g_tx_buffer[l_tx_push_index] = in_dosChar;

	// wait if buffer is full
	while( new_push_index == l_tx_pop_index );

	// store new index
 	l_tx_push_index = new_push_index;

	// initiate transmission
	UCSRB |= _BV(UDRIE);
}

///////////////////////////////////////////////////////////////////////////////
// UART Send Constans String
void drvUARTSendString( const prog_char* in_string )
{
	void* p = (prog_char*)in_string;
	
	while( pgm_read_byte_near(p) != '\0' )
	{
		drvUARTSendChar( pgm_read_byte_near(p) );
		p++;
	}
}

///////////////////////////////////////////////////////////////////////////////
// UART Transmit Complete Interrupt Function
SIGNAL(SIG_UART_DATA)
{
	if( l_tx_pop_index == l_tx_push_index )
	{
		// if no more dosCharacter, stop UART
		UCSRB &= ~_BV(UDRIE);
	}
	else
	{
		// Send next dosCharacter in string
		UDR = g_tx_buffer[l_tx_pop_index];

		// step to the next dosCharacter
		l_tx_pop_index++;
		if( l_tx_pop_index >= drvUART_TX_BUFFER_LENGTH )
			l_tx_pop_index = 0;
	}
}

///////////////////////////////////////////////////////////////////////////////
// Wait for treansmitter empty
void drvUARTWaitForTransmitEmpty(void)
{
	// wait for UART ready
	while( l_tx_pop_index != l_tx_push_index );
}

/*****************************************************************************/
/* Receiver section                                                          */
/*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
// UART Receive Line
void drvUARTReceiveLine(void)
{
	drvUARTIsLineReceived();
	while( !l_line_received );
}

///////////////////////////////////////////////////////////////////////////////
// Is Line Received
dosBool drvUARTIsLineReceived(void)
{
	return l_line_received;
}

///////////////////////////////////////////////////////////////////////////////
// Get Received Character Count
dosByte drvUARTGetReceivedCharacterCount(void)
{
	return l_rx_index;
}

///////////////////////////////////////////////////////////////////////////////
// Clear input buffer
void drvUARTClearInputBuffer(void)
{
	// initialize receive function
	l_rx_index = 0;
	l_line_received = dosFalse;
}

///////////////////////////////////////////////////////////////////////////////
// UART Receive Complete Interrupt Function
SIGNAL(SIG_UART_RECV)
{
	// read dosCharacter
	dosChar ch = UDR;
	
	if( !l_line_received )
	{
		// process dosCharacter
		switch( ch )
		{
			case '\r': // process 'return' dosCharacter
				l_line_received = dosTrue;
				g_rx_buffer[l_rx_index] = '\0';
				break;
					
			case '\b': // process 'backspace' dosCharacter
				if( l_rx_index > 0 )
					l_rx_index--;
				break;
					
			default:
				if( l_rx_index < drvUART_RX_BUFFER_LENGTH - 1 )
				{
					if( ch >= ' ' )
						g_rx_buffer[l_rx_index++] = ch;
				}
				break;
		}
	}
}

